package com.zs.grain.service

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.util.Base64
import android.widget.Toast
import androidx.core.app.ActivityCompat
import com.example.clockkotlin.databaseClockAlarm.AlarmSignal
import com.facebook.drawee.BuildConfig
import javax.inject.Inject
import javax.inject.Singleton

import com.jeremyliao.liveeventbus.LiveEventBus
import com.xnkj1688.semplibrary.entity.Snapshot
import com.xnkj1688.semplibrary.enums.SnapshotType
import com.xnkj1688.semplibrary.ulti.*
import com.zs.grain.tool.clockManage.LocalDataBase
import com.zslibrary.BusMaster.BusMaster
import io.reactivex.Completable
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import java.io.File
import java.io.FileOutputStream
import java.lang.reflect.InvocationTargetException
import java.sql.Date
import java.util.ArrayList


/**
 * Created by Wesley on 2017/7/15.
 */
@Singleton
class SyncService @Inject constructor() {

    @Inject
    lateinit var mContext: Context
    @Inject
    lateinit var mDataService: DataService
    @Inject
    lateinit var mDeviceService: DeviceService
    @Inject
    lateinit var mSignalRService: SignalRService

    var mAppInfo: MachineInfo? = null

    private var mIstop: Boolean = false

    fun init() {
        LiveEventBus.get("SyncService.UpdateAppInfo").observeStickyForever(){
            mAppInfo = this.getVersionInfo(mContext);
            if(mAppInfo!=null) mSignalRService.uploadMachineInfo(mAppInfo!!)
                .subscribe({
                    mAppInfo = null
                },{
                    it.printStackTrace()
                })
        }
        LiveEventBus.get("SignalR.Connected").observeStickyForever() {
            if(mAppInfo!=null) LiveEventBus.get("SyncService.UpdateAppInfo").post("");
        }
        LiveEventBus.get("SyncService.UploadTerminalInfo").observeStickyForever() {
            this.uploadTerminalInfo().subscribe({}, {})
        }
        LiveEventBus.get("SyncService.UploadOrder", Long::class.java)
            .observeStickyForever() {
                this.uploadOrder(it).subscribe({}, {})
            }
        LiveEventBus.get("SyncService.UploadComponents").observeStickyForever() {
            this.uploadComponents().subscribe({}, {})
        }
        LiveEventBus.get("SyncService.UploadSnapshots").observeStickyForever() {
            this.uploadSnapshots().subscribe({}, {})
        }

        LiveEventBus.get("RemotePush.updateSettings").observeStickyForever() {
            //mDeviceService.isMachineRemotePusing = true
            this.syncSettings("RemotePush",false,false,true)
        }
        LiveEventBus.get("RemotePush.updateScene").observeStickyForever() {
            //mDeviceService.isMachineRemotePusing = true
            this.syncSettings("RemotePush",false,true,false)
        }
        LiveEventBus.get("RemotePush.updateAdvertisements").observeStickyForever() {
            this.syncAdvs("RemotePush")
        }
        LiveEventBus.get("SyncService.DeleteMakingProduct").observeStickyForever(){
            LocalDataBase.deleteAlarm("makingProduct")
        }
        LiveEventBus.get("SyncService.AddMakingProduct").observeStickyForever(){
            LocalDataBase.addAlarm("01:02","makingProduct")//防止被自动重启
        }
        LiveEventBus.get("ProductFinsh.checkTask").observeStickyForever(){
            //mDataService.insertLog("ProductFinsh.checkTask")
            Single.just("")
                .subscribe({
            val clocksArray:ArrayList<AlarmSignal> = LocalDataBase.getClocksArray()
            for(ca in clocksArray){
                if(ca.setting.contains("update")){
                    Toast.makeText(mContext, "机器数据更新,已执行...", Toast.LENGTH_SHORT).show()
                    if(ca.setting.contains("updateSetting")){
                        LiveEventBus.get("RemotePush.updateSettings").post(true)
                        LocalDataBase.deleteAlarm("")
                    }
                    if(ca.setting.contains("updateScene")){
                        LiveEventBus.get("RemotePush.updateScene").post(true)
                    }
                    if(ca.setting.contains("updateAdvertisements")){
                        LiveEventBus.get("RemotePush.updateAdvertisements").post(true)
                    }
                }else if(ca.setting.contains("waitingReboot")){
                    if(ca.enable) {
                        Toast.makeText(mContext, "机器即将进入维护状态,10秒后自动重启...", Toast.LENGTH_LONG).show()
                        LocalDataBase.deleteAlarm("waitingReboot")
                        Handler().postDelayed({
                            BusMaster.getInstance(mContext).resetBoard()
                            val componetName = ComponentName(
                                "com.example.reboottest",
                                "com.example.reboottest.MainActivity"
                            );
                            val intent = Intent();
                            intent.setComponent(componetName);
                            mContext.startActivity(intent);
                        }, 10000)
                    }
                }
            }
                }, {})
        }
        this.startUploadThread()
    }

    fun uploadOrder(orderId: Long): Completable {
        return Completable.defer {
            var order = mDataService.getOrder(orderId)
            if (order == null) return@defer Completable.complete()

            Completable.fromSingle<Boolean> { single ->
                mSignalRService.uploadOrder(order)
                    .subscribe({
                        if (it) mDataService.delete(order)
                        LiveEventBus.get("SyncService.UploadTracks").post("")
                        single.onSuccess(it)
                    }, {
                        it.printStackTrace()
                    })
            }
        }
    }

    fun uploadOrders(): Completable {
        return Completable.defer {
            var orders = mDataService.lazyOrders
            for (item in orders) {//OrderSS
                mDataService.insertLog("Order:订单上传检查,服务端单号:" + item.id + ",机器生成单号:" + mDataService.currentOrder?.id)
                if (item.id == mDataService.currentOrder?.id) continue
                var order = mDataService.getOrder(item.id)
                if (order == null) continue
                try {//TODO:jerry add try catch
                    var single = mSignalRService.uploadOrder(order)
                    single.subscribe({
                        if (it) mDataService.delete(order)
                    }, {
                        it.printStackTrace()
                    })
                    var error = Completable.fromSingle(single).blockingGet()
                    if (error != null) return@defer Completable.error(error)
                }catch (e:Exception){
                    e.printStackTrace()
                }
            }
            Completable.complete()
        }
    }

    fun uploadComponents(): Completable {
        return Completable.defer {
            var components = mDataService.mUpdateComponents
            if (components.size <= 0) return@defer Completable.complete()

            Completable.fromSingle<Boolean> { single ->
                var timestamp = mDataService.componentTimestamp
                mSignalRService.uploadComponents(components)
                    .subscribe({
                        if (it && timestamp == mDataService.componentTimestamp) {
                            mDataService.componentTimestamp = null
                            mDataService.mUpdateComponents.clear();
                        }
                    }, {
                        it.printStackTrace()
                    })
            }
        }
    }

    fun uploadAdjustments(): Completable {
        return Completable.defer {
            var adjustments = mDataService.mUpdateAdjustments
            if (adjustments.size <= 0) return@defer Completable.complete()
            adjustments = mDataService.adjustments

            Completable.fromSingle<Boolean> { single ->
                var timestamp = mDataService.adjustmentTimestamp
                mSignalRService.uploadAdjustments(adjustments)
                    .subscribe({
                        if (it && timestamp == mDataService.adjustmentTimestamp) {
                            mDataService.adjustmentTimestamp = null
                            mDataService.mUpdateAdjustments.clear();
                        }
                    }, {
                        it.printStackTrace()
                    })
            }
        }
    }

    fun uploadSnapshots(snapshots: List<Snapshot>): Completable {
        if (snapshots.size <= 0) return Completable.complete()

        var single = mSignalRService.uploadSnapshots(snapshots)
        single.subscribe({
            if (it) mDataService.deleteSnapshots(snapshots);
        }, {
            it.printStackTrace()
        })
        return Completable.fromSingle(single)
    }

    fun uploadSnapshots(): Completable {
        return Completable.defer {
            var snapshots = mDataService.lazySnapshots
            val list = ArrayList<Snapshot>()
            for (item in snapshots) {
                item.tracks = mDataService.getTracksFromDB(item.id)
                list.add(item)
                if (item.type == SnapshotType.Load.ordinal) {
                    var error = this.uploadSnapshots(list).blockingGet()
                    if (error != null) return@defer Completable.error(error)
                }
            }
            Completable.complete()
        }
    }

    fun uploadCashLogs(): Completable {
        return Completable.defer {
            var logs = mDataService.getCashLogs(10)
            if (logs == null || logs.size <= 0) return@defer Completable.complete()

            var single = mSignalRService.uploadCashLogs(logs)
            single.subscribe({
                if (it) mDataService.deleteCashLogs(logs);
            }, {
                it.printStackTrace()
            })
            var error = Completable.fromSingle(single).blockingGet()
            if (error != null) return@defer Completable.error(error)

            Completable.complete()
        }
    }

    fun uploadSystemLogs(): Completable {
        return Completable.defer {
            var logs = mDataService.getUnsyncSystemLogs(20)
            if (logs == null || logs.size <= 0) return@defer Completable.complete()

            var single = mSignalRService.uploadSystemLogs(logs)
            single.subscribe({
                if (it) {
                    for (item in logs) item.isUploaded = true
                    mDataService.DaoSystemLog.updateInTx(logs)
                };
            }, {
                it.printStackTrace()
            })
            var error = Completable.fromSingle(single).blockingGet()
            if (error != null) return@defer Completable.error(error)

            Completable.complete()
        }
    }

    fun uploadTerminalInfo(): Completable {
        return uploadOrders()
            .andThen(uploadComponents())
            .andThen(uploadSnapshots())
            .andThen(uploadCashLogs())
            .andThen(uploadSystemLogs())
    }

    fun startUploadThread() {
        mIstop = false
        Single.just("")
            .subscribeOn(Schedulers.io())
            .subscribe({
                while (!mIstop) {
                    if (mSignalRService.isConnected) {
                        uploadTerminalInfo().subscribe({}, {})
                        Thread.sleep(60 * 1000)
                    } else {
                        Thread.sleep(10 * 1000)
                    }
                }
            }, {
                it.printStackTrace()
            })
    }

    fun stopUploadThread() {
        mIstop = true
    }


    private fun getVersionInfo(context: Context): MachineInfo {
        val info = MachineInfo()
        info.id = AppUtil.getUniqueID(context);
        info.apiLevel = Build.VERSION.SDK_INT
        info.applicationId = context.applicationInfo.processName
        val packageInfo =
            context.packageManager.getPackageInfo(context.packageName, 0)
//        info.Version = packageInfo.versionName + "." + packageInfo.versionCode
//        info.FLAVOR = packageInfo.versionName.subSequence(0,1).toString()//BuildConfig.FLAVOR;
//mDataService.insertLog("动态权限申请结果:"+ActivityCompat.checkSelfPermission(
//    context,
//    "android.permission.READ_PHONE_STATE"
//).toString()+",flavor:"+info.FLAVOR)
        info.version = packageInfo.versionName + "." + packageInfo.versionCode
        info.flavor = BuildConfig.FLAVOR;
        if (ActivityCompat.checkSelfPermission(
                context,
                "android.permission.READ_PHONE_STATE"
            ) == 0
        ) {
            val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
            info.IMEI = telephonyManager.deviceId
            info.IMSI = telephonyManager.subscriberId
            info.ICCID = telephonyManager.simSerialNumber
            if (Build.VERSION.SDK_INT >= 22) {
                val subInfo =
                    SubscriptionManager.from(context).getActiveSubscriptionInfoForSimSlotIndex(0)
                if (subInfo != null) info.ICCID = subInfo.iccId
            }
            try {
                val method =
                    telephonyManager.javaClass.getDeclaredMethod("getSubscriberInfo")
                try {
                    method.isAccessible = true
                    val obj = method.invoke(telephonyManager)
                    val method2 =
                        obj.javaClass.getDeclaredMethod("getPhone", Integer.TYPE)
                    method2.isAccessible = true
                    val obj2 = method2.invoke(obj, 0)
                    val method3 =
                        obj2.javaClass.getMethod("getFullIccSerialNumber")
                    info.ICCID = method3.invoke(obj2) as String
                } catch (var11: IllegalAccessException) {
                    var11.printStackTrace()
                } catch (var12: InvocationTargetException) {
                    var12.printStackTrace()
                }
            } catch (var13: NoSuchMethodException) {
                var13.printStackTrace()
            }
        }

        var attachments = arrayListOf<String>()
        attachments.add("IoAppVersion:${mDeviceService.ioAppVersion}")
        attachments.add("IoBootVersion:${mDeviceService.ioBootVersion}")
        attachments.add("CupAppVersion:${mDeviceService.cupAppVersion}")
        attachments.add("CupBootVersion:${mDeviceService.cupBootVersion}")
        attachments.add("CraftVersion:${mDeviceService.craftVersion}")
        info.attachments = attachments.toTypedArray()

        return info;
    }

    fun syncAdvs(getType:String){
        if(!mDeviceService.isMakingProduct) {
            mSignalRService.settings
                .map {
                    var server = mDataService.server;
                    if (server != mDataService.server)
                        mSignalRService.init("http://" + mDataService.server)
                    it.size
                }
                .map {
                }
                .flatMap {
                    mSignalRService.advertisements
                }.map {
                    mDataService.DaoAdvertisement.deleteAll()
                    mDataService.DaoAdvertisement.insertInTx(it.toList())
                    it.size
                }.map {
                    LocalDataBase.deleteAlarm("updateAdvertisements")
                    mDataService.insertLog("$getType:广告数据同步成功(${it}条)")
                }.subscribe({
                    var bundle = Bundle()
                    bundle.putString("UI", "Menus")
                    LiveEventBus.get("SwitchUI").post(bundle)
                }, {
                    mDataService.insertLog("$getType:广告数据同步失败")
                })
            LiveEventBus.get("RemotePush.refreshPlayer").post(true)
        }else{
            mDataService.insertLog("$getType:产品制作中,待完成后进行广告同步")
            LocalDataBase.addAlarm("01:06","updateAdvertisements")
        }
    }

    fun syncSettings(getType:String,mcb:Boolean,pcb:Boolean,scb:Boolean){
        if (pcb) {
            if(!mDeviceService.isMakingProduct) {
                var bundle = Bundle()
                bundle.putString("UI", "Temp")
                bundle.putInt("TempType", 3)
                LiveEventBus.get("SwitchUI").post(bundle)

                mSignalRService.settings
                    .flatMap {
                        mSignalRService.components
                    }.map {
                        if (it.size <= 0) {
                            var components = mDeviceService.components
                            mDataService.update(components)
                            LiveEventBus.get("SyncService.UploadComponents").post(0)
                        } else {
                            mDataService.DaoComponent.deleteAll()
                            mDataService.DaoComponent.insertInTx(it.toList())
                        }
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("部件数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:部件数据同步成功(${it}条)")
                    }.flatMap {
                        mSignalRService.adjustments
                    }.map {
                        mDataService.DaoAdjustment.deleteAll()
                        mDataService.DaoAdjustment.insertInTx(it.toList())
                        mDeviceService.setAdjustments()
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("部件参数同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:部件参数同步成功(${it}条)")
                    }


                    .flatMap {
                        mSignalRService.menus
                    }.map {
                        mDataService.DaoMenu.deleteAll()
                        mDataService.DaoMenu.insertInTx(it.toList())
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("菜单数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:菜单数据同步成功(${it}条)")
                    }.flatMap {
                        mSignalRService.categories
                    }.map {
                        mDataService.DaoCategory.deleteAll()
                        mDataService.DaoCategory.insertInTx(it.toList())
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("分类数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:分类数据同步成功(${it}条)")
                    }.flatMap {
                        mSignalRService.goods
                    }.map {
                        mDataService.DaoGood.deleteAll()
                        mDataService.DaoGood.insertInTx(it.toList())
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("商品数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:商品数据同步成功(${it}条)")
                    }.flatMap {
                        mSignalRService.ingridents
                    }.map {
                        mDataService.DaoIngrident.deleteAll()
                        mDataService.DaoIngrident.insertInTx(it.toList())
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("原料数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:商品数据同步成功(${it}条)")
                    }
                    .flatMap {
                        mSignalRService.receipes
                    }.map {
                        mDataService.DaoReceipe.deleteAll()
                        mDataService.DaoReceipe.insertInTx(it.toList())
                        it.size
                    }.map {
                        //uiNotify.postValue(super.getToastNotify("配方数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:配方数据同步成功(${it}条)")
                    }
                    .flatMap {
                        mSignalRService.images
                    }.map {
                        mDataService.DaoImage.deleteAll()
                        mDataService.DaoImage.insertInTx(it.toList())
                        it
                    }.map {
                        //uiNotify.postValue(getToastNotify("图片数据同步成功(${it.size})条)", Toast.LENGTH_SHORT))//size
                        mDataService.insertLog("$getType:图片数据同步成功(${it.size})条)")//
                        it
                    }.toObservable()
                    .flatMap {
                        var dir = File(mDataService.imagesPath)
                        if (!dir.exists()) dir.mkdir()
                        Observable.fromIterable(it.toList())
                    }.observeOn(Schedulers.newThread())
                    .map {
                        val fileName = "${mDataService.imagesPath}/${it.getFileName()}"
                        var file = File(fileName)
                        if ((file.exists() && it.remark == null) || (it.remark != null && file.lastModified() >= Date.valueOf(
                                it.remark
                            ).getTime())
                        )
                            return@map

                        var single = mSignalRService.getImage(it.id)
                        single.map {
                            var data = Base64.decode(it, Base64.NO_WRAP);
                            var fos = FileOutputStream(file);
                            fos.write(data);
                            fos.close();
                        }.subscribe({
                            //uiNotify.postValue(getToastNotify("图片下载成功:\n${file.name}", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:图片下载成功:${file.name}")
                        }, {
                            //uiNotify.postValue(super.getToastNotify("图片下载失败:\n${file.name}", Toast.LENGTH_LONG))
                            mDataService.insertLog("$getType:图片下载失败:${file.name}")
                        })
                        Completable.fromSingle(single).blockingGet()
                    }
                    .subscribe({
                    }, {
                        //uiNotify.postValue(super.getToastNotify("产品数据同步失败", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:菜单、组件数据同步失败")
                    }, {
                        //uiNotify.postValue(super.getToastNotify("产品数据同步成功", Toast.LENGTH_SHORT))
                        mDataService.insertLog("$getType:菜单、组件数据同步成功")
                        LocalDataBase.deleteAlarm("updateScene")
                        var bundle = Bundle()
                        bundle.putString("UI", "Menus")
                        LiveEventBus.get("SwitchUI").post(bundle)
                    })
            }else{
                mDataService.insertLog("$getType:产品制作中,待完成后进行菜单、组件数据同步")
                LocalDataBase.addAlarm("01:04","updateScene")
            }
        }

            if (scb) {
                if(!mDeviceService.isMakingProduct) {
                    var bundle = Bundle()
                    bundle.putString("UI", "Temp")
                    bundle.putInt("TempType", 3)
                    LiveEventBus.get("SwitchUI").post(bundle)
                    mSignalRService.settings
                        .map {
                            var server = mDataService.server;

                            mDataService.DaoSetting.deleteAll();
                            mDataService.DaoSetting.insertInTx(it.toList());
                            if (server != mDataService.server)
                                mSignalRService.init("http://" + mDataService.server)

                            mDeviceService.init();
                            it.size
                        }
                        .map {
                            //uiNotify.postValue(super.getToastNotify("终端配置同步成功(${it}条)", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:终端配置同步成功(${it}条)")
                        }.map {
                            //                        mDataService.DaoAdvertisement.deleteAll()
//                        mDataService.DaoAdvertisement.insertInTx(it.toList())
//                        it.size
                        }.map {
                            //uiNotify.postValue(super.getToastNotify("广告数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                            //mDataService.insertLog("$getType:广告数据同步成功(${it}条)")
                        }.subscribe({
                            //uiNotify.postValue(super.getToastNotify("系统数据同步成功", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:系统数据同步成功")
                            LocalDataBase.deleteAlarm("updateSetting")
                            var bundle = Bundle()
                            bundle.putString("UI", "Menus")
                            LiveEventBus.get("SwitchUI").post(bundle)
                        }, {
                            //uiNotify.postValue(super.getToastNotify("系统数据同步失败",Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:系统数据同步失败")
                        })
                }else{
                    mDataService.insertLog("$getType:产品制作中,待完成后进行设置同步")
                    LocalDataBase.addAlarm("01:03","updateSetting")
                }
            }

            if (mcb) {
                if (!mDeviceService.isMakingProduct) {
                    var bundle = Bundle()
                    bundle.putString("UI", "Temp")
                    bundle.putInt("TempType", 3)
                    LiveEventBus.get("SwitchUI").post(bundle)
                    mSignalRService.settings
                        .flatMap {
                            mSignalRService.components
                        }.map {
                            if (it.size <= 0) {
                                var components = mDeviceService.components
                                mDataService.update(components)
                                LiveEventBus.get("SyncService.UploadComponents").post(0)
                            } else {
                                mDataService.DaoComponent.deleteAll()
                                mDataService.DaoComponent.insertInTx(it.toList())
                            }
                            it.size
                        }.map {
                            //uiNotify.postValue(super.getToastNotify("部件数据同步成功(${it}条)", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:部件数据同步成功(${it}条)")
                        }.flatMap {
                            mSignalRService.adjustments
                        }.map {
                            mDataService.DaoAdjustment.deleteAll()
                            mDataService.DaoAdjustment.insertInTx(it.toList())
                            mDeviceService.setAdjustments()
                            it.size
                        }.map {
                            //uiNotify.postValue(super.getToastNotify("部件参数同步成功(${it}条)", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:部件参数同步成功(${it}条)")
                        }
                        .subscribe({
                            //uiNotify.postValue(super.getToastNotify("机器数据同步成功", Toast.LENGTH_LONG))
                            mDataService.insertLog("$getType:机器数据同步成功")
                            //mDeviceService.isMachineRemotePusing = false
                            LocalDataBase.deleteAlarm("updateMachine")
                            var bundle = Bundle()
                            bundle.putString("UI", "Menus")
                            LiveEventBus.get("SwitchUI").post(bundle)
                        }, {
                            //uiNotify.postValue(super.getToastNotify("机器数据同步失败", Toast.LENGTH_SHORT))
                            mDataService.insertLog("$getType:机器数据同步失败")
                        })
                }else{
                    mDataService.insertLog("$getType:产品制作中,待完成后进行设备同步")
                    LocalDataBase.addAlarm("01:05","updateMachine")
                }
            }
    }

    companion object {
        val TAG = "SyncService"
    }
}
